#include <iostream>
#include <sstream>
#include <fstream>
#include <sys/time.h>
#include "pthread.h"
#include "ServerSocket.h"
#include "SocketException.h"
#include <string>


pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
long long theTickCount = 0;
double latestAverage = 0;

// This should probably be put as a commandline-parameter or removed
//
long long theMeter = 12194;
long theMeterTicks = 0;

void *task_reader(void *X) //Thread that reads data from the serial-device
{

      // Which device should the pulses be read from?
      const char* THEFILE = "/dev/ttyS0";

      // General settings
      const int TICKS_PER_KWH = 10000;
      const int SAMPLE_SIZE = 20;

      // Constants, no need to change them
      const int TICKS_PER_WH = TICKS_PER_KWH / 1000;
      const long MS_PER_HOUR = 3600000L;

      char theChar;

      std::ifstream indata;

      // Open the device/file
      indata.open(THEFILE);
      if(!indata)
      {
	// file couldn't be opened
        std::cerr << "Error: file could not be opened" << std::endl;
        exit(1);
      }

      timeval t1, t2;
      double elapsedTime;

      // Read one pulse before starting to measure
      // so that we know that we start the measuring exactly
      // after a pulse.
      indata >> theChar;
      pthread_mutex_lock( &mutex1 );
      theTickCount++;
      pthread_mutex_unlock( &mutex1 );
      
      // Start looping forever
      while(true)
      {
        // Get initial time
        gettimeofday(&t1, NULL);

        // Read as many pulses as defined by SAMPLE_SIZE
        for (int x=0; x < SAMPLE_SIZE; x++)
        {
	    theChar=' ';
            indata >> theChar;
	    // Test for valid data
            if (theChar != ' ')
	    {
                pthread_mutex_lock( &mutex1 );
                // Update counters
                theTickCount++;
	        theMeterTicks++;
	        if (theMeterTicks == TICKS_PER_KWH)
	        {
                    // Update meter-reading
		    theMeterTicks = 0;
		    theMeter++;
	        }
            pthread_mutex_unlock( &mutex1 );
	    }
        }
        // Get time after collecting the samples
        gettimeofday(&t2, NULL);

        // Calculate elapsed time
        elapsedTime = (t2.tv_sec - t1.tv_sec) * 1000.0;      // sec to ms
        elapsedTime += (t2.tv_usec - t1.tv_usec) / 1000.0;   // us to ms

        pthread_mutex_lock( &mutex1 );
        // Calculate and store the average energy
        latestAverage = (MS_PER_HOUR/elapsedTime) * (SAMPLE_SIZE/TICKS_PER_WH);
        pthread_mutex_unlock( &mutex1 );
      }
      return(0);
}

void *task_printer(void *X) //Prints out current readings
{
    while (true)
    {
        std::cout << "Ticks: tickcount=" << theTickCount << " Meter=" << theMeter << " Average=" << latestAverage << " W" << std::endl;
        usleep(1000000);
    }
    return(0);
}

// The thread for accepting TCP/IP connections on a socket and outputting the current readings.
void *task_network(void *X)
{
    try
    {
       // Create the server socket
       ServerSocket server ( 30000 );
       while ( true )
       {
	  ServerSocket new_sock;
	  server.accept ( new_sock );
	  std::ostringstream theStream (std::ostringstream::out);
          theStream << theTickCount << " " << latestAverage << "\n";
	  new_sock << theStream.str();
       }

    }
    catch ( SocketException& e )
    {
       std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
    }

}

int main() {
      pthread_t ThreadA,ThreadB,ThreadC; // declare threads

      pthread_create(&ThreadA,NULL,task_reader,(void*)NULL); // create threads
      pthread_create(&ThreadB,NULL,task_printer,(void*)NULL);
      pthread_create(&ThreadC,NULL,task_network,(void*)NULL);

      // wait for threads, this is not used right now because of the 
      // while(true) in the threads but could be useful later. 
      pthread_join(ThreadA,NULL);
      pthread_join(ThreadB,NULL);
      pthread_join(ThreadC,NULL);

      return(0);
}
